#ifndef PAGING_API_H
#define PAGING_API_H

#include "types.h"


// Paging flags
#define PF_PRESENT      1
#define PF_WRITE        (1 << 1)
#define PF_USER         (1 << 2)
#define PF_PWT          (1 << 3)
#define PF_PCD          (1 << 4)
#define PF_ACCESSED     (1 << 5)
#define PF_DIRTY        (1 << 6)
#define PF_PAT_PTE      (1 << 7) // Only for pte
#define PF_MAP_PAGE     (1 << 7) // Page size bit. for all except pte paging structs entries
#define PF_GLOBAL       (1 << 8)
#define PF_PAT12        (1 << 12) // PAT bit. for all except pte paging structs entries

#define PF_UNEXECUTABLE (1llu << 63)

// addition for page fault:
#define PF_RCVD         (1 << 3)
#define PF_INSTRUCTION  (1 << 4)

struct vaddr32_4kb {
	u32 offset   : 12;
	u32 i_pte    : 10;
	u32 i_pde    : 10;
};

struct vaddr64_4kb {
  u64 offset   : 12;
  u64 i_pte    : 9;
  u64 i_pde    : 9;
  u64 i_pdpte  : 9;
  u64 i_pml4e  : 9;
  u64 reserved : 16;
};


struct vaddr64_2mb {
  u64 offset   : 21;
  u64 i_pde    : 9;
  u64 i_pdpte  : 9;
  u64 i_pml4e  : 9;
  u64 reserved : 16;
};


struct vaddr64_1gb {
  u64 offset   : 30;
  u64 i_pdpte  : 9;
  u64 i_pml4e  : 9;
  u64 reserved : 16;
};


#define I_PML4E_64(x) ((struct vaddr64_4kb *)&(x))->i_pml4e
#define I_PDPTE_64(x) ((struct vaddr64_4kb *)&(x))->i_pdpte
#define I_PDE_64(x) ((struct vaddr64_4kb *)&(x))->i_pde
#define I_PTE_64(x) ((struct vaddr64_4kb *)&(x))->i_pte

#define OFFSET_64_4KB(x) ((struct vaddr64_4kb *)&(x))->offset
#define OFFSET_64_2MB(x) ((struct vaddr64_2mb *)&(x))->offset
#define OFFSET_64_1GB(x) ((struct vaddr64_1gb *)&(x))->offset


#define PTE_64_SHIFT 12
#define PDE_64_SHIFT (12+9)
#define PDPTE_64_SHIFT (12+9+9)
#define PML4E_64_SHIFT (12+9+9+9)

#define PAGE_SHIFT_4KB 12
#define PAGE_SHIFT_2MB (12+9)
#define PAGE_SHIFT_1GB (12+9+9)

#define OFFSET_MASK_4KB ((1<<PAGE_SHIFT_4KB)-1)
#define OFFSET_MASK_2MB ((1<<PAGE_SHIFT_2MB)-1)
#define OFFSET_MASK_1GB ((1<<PAGE_SHIFT_1GB)-1)



#define MASK(begin, end) ((1ull<<(end)) - (1ull<<(begin)))

#define ADDR_MASK_64_4KB MASK(12, 52)
#define ADDR_MASK_64_2MB MASK(13, 52)
#define ADDR_MASK_64_1GB MASK(13, 52)

  

extern inline u64 vaddr64_3 (u16 i_pml4e, u16 i_pdpte, u32 offset)
{
  return (u64)i_pml4e << PML4E_64_SHIFT |
         (u64)i_pdpte << PDPTE_64_SHIFT |
         (u64)offset;
}



extern inline u64 vaddr64_4 (u16 i_pml4e, u16 i_pdpte, u16 i_pde, u32 offset)
{
  return (u64)i_pml4e << PML4E_64_SHIFT |
         (u64)i_pdpte << PDPTE_64_SHIFT |
         (u64)i_pde   << PDE_64_SHIFT |
         (u64)offset;
}



extern inline u64 vaddr64_5 (u16 i_pml4e, u16 i_pdpte, u16 i_pde, u16 i_pte, u16 offset)
{
  return (u64)i_pml4e << PML4E_64_SHIFT |
         (u64)i_pdpte << PDPTE_64_SHIFT |
         (u64)i_pde   << PDE_64_SHIFT |
         (u64)i_pte   << PTE_64_SHIFT |
         (u64)offset;
}

/// one mode overlay PAE ???

#ifdef CONFIG_X86_64

#define set_paging_entry set_pse64
typedef u64 pse_t;
//typedef u64 vaddr_t;

#else

#define set_paging_entry set_pse32
typedef u32 pse_t; // PAE ??
//typedef u32 vaddr_t;

#endif

void *get_first_paging_struct(void);

#endif /* PAGING_API_H */